#include "basic.hpp"
#include "logger.hpp"
#include <fcntl.h>

namespace oraysock5 {
int executable_path(char* processdir, char* processname, size_t len)
{
    char* path_end;
    if (readlink("/proc/self/exe", processdir, len) <= 0) {
        THROW_EXCEPTION("call readlink");
    }
    path_end = strrchr(processdir, '/');
    if (path_end == nullptr) {
        THROW_EXCEPTION("找不到'/'");
    }
    ++path_end;
    strcpy(processname, path_end);
    *path_end = '\0';
    return static_cast<int>(path_end - processdir);
}

int mkdir_recursive(const std::string& pathname)
{
    if (pathname.empty())
        return -1;
    char* path_dup = strdup(pathname.c_str());
    size_t len = strlen(path_dup);
    if (len == 0) {
        return -1;
    }
    size_t i = path_dup[0] == '/' ? 1 : 0;
    for (; i <= len; ++i) {
        if (path_dup[i] == '/' || path_dup[i] == '\0') {
            char ch = path_dup[i];
            path_dup[i] = '\0';
            if (::mkdir(path_dup, 0755) == -1 && errno != EEXIST) {
                free(path_dup);
                return -1;
            }
            path_dup[i] = ch;
        }
    }
    free(path_dup);
    return 0;
}

std::string& trim(std::string& xstr, std::function<int(int)> cb)
{
    if (xstr.empty()) {
        return xstr;
    }

    std::string::iterator c = xstr.begin();
    for (; c != xstr.end() && cb(*c++);)
        ;
    xstr.erase(xstr.begin(), --c);

    for (c = xstr.end(); c != xstr.begin() && cb(*--c);)
        ;
    xstr.erase(++c, xstr.end());

    return xstr;
}

int daemon()
{
    if (::daemon(1, 0) == -1) {
        THROW_EXCEPTION("call daemon");
    }
    return getpid();
}

void print_exception(const std::exception& e, int level)
{
    std::ostringstream info;
    info << "异常层级: " << level << " 异常信息: \n";
    info << e.what();
    LOG_WARN << info.str();
    try {
        if (auto ptr = dynamic_cast<const std::nested_exception*>(&e)) {
            if (ptr->nested_ptr())
                std::rethrow_exception(ptr->nested_ptr());
        }
    } catch (const std::exception& ex) {
        print_exception(ex, level + 1);
    } catch (...) {
    }
}

void writepid(const char* pidfile)
{
    bool isfree = false;
    if (pidfile == nullptr) {
        char path[PATH_MAX] = { 0 };
        char processname[NAME_MAX] = { 0 };
        executable_path(path, processname, sizeof(path));
        char* processname_end = strrchr(processname, '.');
        *processname_end = '\0';
        pidfile = new char[NAME_MAX];
        sprintf(const_cast<char*>(pidfile), "./%s.pid", processname);
        isfree = true;
    }
    int fp = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
    if (isfree) {
        delete[] pidfile;
    }
    if (fp == -1) {
        close(fp);
        THROW_EXCEPTION("call open");
    }
    if (lockf(fp, F_TLOCK, 0) < 0) {
        close(fp);
        THROW_EXCEPTION("call lockf");
    }

    char buf[64] = { 0 };
    pid_t pid = ::getpid();
    snprintf(buf, 64, "%d\n", pid);
    size_t len = strlen(buf);
    ::write(fp, buf, len);
    close(fp);
}
}
